#include "stm32f4xx.h"                  // Device header
#include "stm32f4xx_conf.h"

/*
 *SPI模式选择:
 *
 *CPOL(时钟极性):
 *		0-空闲状态时,SCK=0
 *		1-空闲状态时,SCK=1
 *CPHA(时钟相位):
 *		0-SCK第一个边沿移入数据,第二个边沿移出数据
 *		1-SCK第一个边沿移出数据,第二个边沿移入数据
 */

#define SPI_SS(x)	GPIO_WriteBit(GPIOB,GPIO_Pin_12,(BitAction)(x))//写SS引脚

/*
 *函数简介:硬件SPI初始化
 *参数说明:无
 *返回类型:无
 *备注:默认使用SPI2,默认SPI模式0
 *备注:默认SS为SPI2_CS(PB12),SCK为SPI2_CLK(PB13),MOSI为SPI2_MOSI(PB15),MISO为SPI2_MISO(PB14)
 *备注:若修改SS引脚,除了修改上方宏定义,此函数GPIO初始化也要修改
 */
void SPI_SPIInit(void)
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);//开启时钟
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//默认上拉
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);//配置SS
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;//复用推挽
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13 |GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_Init(GPIOB,&GPIO_InitStructure);//配置SCK、MISO和MOSI
	
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource13,GPIO_AF_SPI2);//开启PB13的SPI2复用模式
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource14,GPIO_AF_SPI2);//开启PB14的SPI2复用模式
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource15,GPIO_AF_SPI2);//开启PB15的SPI2复用模式
	
	SPI_InitTypeDef SPI_InitStructure;
	SPI_InitStructure.SPI_Mode=SPI_Mode_Master;//主机模式
	SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;//双线全双工
	SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;//8位数据帧
	SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;//高位先行
	SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_2;//波特率预分频系数2,SCK频率为PCLK/分频系数,即Freq_SCK=42MHz/2=21MHz
	SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low;//CPOL=0
	SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;//第一个边沿开始采样,即CPHA=0
	SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;//软件NSS模式
	SPI_InitStructure.SPI_CRCPolynomial=7;//CRC检测,不懂
	SPI_Init(SPI2,&SPI_InitStructure);
	
	SPI_Cmd(SPI2,ENABLE);//使能SPI2
	
	SPI_SS(1);//复位SS
}

/*
 *函数简介:硬件SPI数据交换开始
 *参数说明:无
 *返回类型:无
 *备注:即拉低SS完成片选
 */
void SPI_Start(void)
{
	SPI_SS(0);
}

/*
 *函数简介:硬件SPI数据交换结束
 *参数说明:无
 *返回类型:无
 *备注:即拉高SS结束片选
 */
void SPI_Stop(void)
{
	SPI_SS(1);
}

/*
 *函数简介:硬件SPI交换一次8bits数据
 *参数说明:8bits发送数据
 *返回类型:8bits接收数据
 *备注:无
 */
uint8_t SPI_Swap8bits(uint8_t SendByte)
{
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)!=SET);//发送缓冲区为空
	SPI_I2S_SendData(SPI2,SendByte);//SPI发送数据(放入发送缓冲区)
	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)!=SET);//接收缓冲区非空,即移位完成	
	return SPI_I2S_ReceiveData(SPI2);//SPI接收数据
}

/*
 *函数简介:硬件SPI交换一个字节数据
 *参数说明:8bits地址
 *参数说明:8bits发送数据
 *返回类型:8bits接收数据
 *备注:在第一个数据发送时,第二个数据已经在发送缓冲区等待以实现连续数据流
 */
uint8_t SPI_SwapByte(uint8_t Address,uint8_t SendByte)
{
	SPI_Start();//开始
	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)!=SET);//发送缓冲区为空
	SPI_I2S_SendData(SPI2,Address);//将地址数据放入发送缓冲区
	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)!=SET);//发送缓冲区为空
	SPI_I2S_SendData(SPI2,SendByte);//SPI发送数据(放入发送缓冲区,实现连续数据流)
	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)!=SET);//接收缓冲区非空,即第一个数据(地址数据)移位完成
	SPI_I2S_ReceiveData(SPI2);//此数据来自发送地址,故读取当前接收缓冲区以清除接收数据和标志位
	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)!=SET);//接收缓冲区非空,即第二个数据(接收数据)移位完成
	SPI_Stop();//结束
	return SPI_I2S_ReceiveData(SPI2);//SPI接收数据
}

/*
 *函数简介:硬件SPI交换数据
 *参数说明:8bits地址
 *参数说明:发送数据数组
 *参数说明:发送数据数组的长度
 *参数说明:接收数据数组
 *返回类型:无
 *备注:在前一个数据发送时,后一个数据已经在发送缓冲区等待以实现连续数据流
 *备注:发送数据数组和接收数据数组长度最好一致
 */
void SPI_SwapData(uint8_t Address,uint8_t *SendData,uint16_t Length,uint8_t *ReceiveData)
{
	SPI_Start();//开始
	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)!=SET);//发送缓冲区为空
	SPI_I2S_SendData(SPI2,Address);//将地址数据放入发送缓冲区
	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)!=SET);//发送缓冲区为空
	SPI_I2S_SendData(SPI2,SendData[0]);//SPI发送第一个数据(放入发送缓冲区,实现连续数据流)
	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)!=SET);//接收缓冲区非空,即地址数据移位完成
	SPI_I2S_ReceiveData(SPI2);//此数据来自发送地址,故读取当前接收缓冲区以清除接收数据和标志位
	
	for(uint16_t i=0;i<Length-1;i++)
	{
		while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)!=SET);//发送缓冲区为空
		SPI_I2S_SendData(SPI2,SendData[i+1]);//在前一个数据发送时,将下一个数据放在发送缓冲区等待以实现连续数据流
		
		while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)!=SET);//接收缓冲区非空,即上一个数据移位完成	
		ReceiveData[i]=SPI_I2S_ReceiveData(SPI2);//接收上一个发送数据的接收数据
	}
	
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)!=SET);//接收缓冲区非空,即最后一个数据移位完成	
	ReceiveData[Length-1]=SPI_I2S_ReceiveData(SPI2);//接收最后一个发送数据的接收数据
	
	SPI_Stop();//结束
}
